#
# lxml compatibility module for ElementTree
#
# Much of this code is copied and modified from Fredrik Lundh's 
# ElementTree package (http://effbot.org/zone/element-index.htm)
# version 1.3.0, distributed with Python 2.7.
# This is not (yet) a complete re-implementation, just enough to
# parse and write simple xml documents with an interface compatible
# with with Stefan Behnel's lxml package (http://lxml.de/).
# In particular, it does not support external entity references,
# parameter entity references, or canonical serialization.
#
# Copyright (c) 2011 by Sean Sherrard.  All rights reserved.
#

__all__ = ['Element', 'SubElement', 'ElementTree', 'XMLParser',
		   'iselement', 'Entity', 'Comment', 'PI', 'CDATA',
		   'parse', 'fromstring', 'tostring']

try:
	from __main__ import USE_CELEMENTTREE
except ImportError:
	USE_CELEMENTTREE = True

if USE_CELEMENTTREE:
	from xml.etree.cElementTree import Element, SubElement, iselement, VERSION
	ETREE_VERSION = 'cElementTree %s' % VERSION
else:
	from xml.etree.ElementTree import Element, SubElement, iselement, VERSION
	ETREE_VERSION = 'ElementTree %s' % VERSION
del VERSION

from xml.etree.ElementTree import Comment, PI, HTML_EMPTY, ElementTree as _ElementTree
from xml.parsers import expat
import io

EXPAT_VERSION = expat.EXPAT_VERSION.replace('_', ' ')

def parse(source, parser=None):
	tree = ElementTree()
	tree.parse(source, parser)
	return tree

def fromstring(text, parser=None):
	if parser is None:
		parser = XMLParser()
	parser.feed(text)
	return parser.close()

def tostring(elem, encoding=None, method=None,
			 # lxml arguments
			 xml_declaration=None, pretty_print=False,
			 with_tail=True, standalone=None):
	if not iselement(elem):
		raise TypeError('%r object is not an Element' % type(elem).__name__)
	if method is None:
		method = 'xml'
	elif method not in ('xml', 'html', 'text'):
		raise ValueError('unknown method %r' % method)
	ios = io.StringIO()
	write = ios.write
	if method == 'xml':
		_serialize_xmldecl(None, write, pretty_print)
	_serialize_elem(elem, write, pretty_print, method)
	if with_tail and elem.tail:
		write(elem.tail)
	text = ios.getvalue()
	ios.close()
	if encoding is unicode:
		return text
	else:
		return text.encode(encoding=encoding, errors='xmlcharrefreplace')

#	data = []
#	class empty: pass
#	buf = empty()
#	buf.write = data.append
#	ElementTree(elem).write(buf, encoding=encoding, method=method,
#							xml_declaration=xml_declaration, pretty_print=pretty_print,
#							with_tail=with_tail, standalone=standalone)
#	return ''.join(data)

class XMLParser(object):

	factory = Element

	def __init__(self, target=None, encoding=None,
				 #lxml arguments
				 attribute_defaults=False, remove_blank_text=False,
				 resolve_entities=True, remove_comments=False,
				 remove_pis=False, strip_cdata=True):
		if target is not None:
			self.__start = target.start
			self.__data = target.data
			self.__stop = target.end
			self.__close = target.close
			self._mytarget = False
		else:
			self._mytarget = True
		self._encoding = encoding
		self._attribute_defaults = attribute_defaults
		self._remove_blank_text = remove_blank_text
		self._resolve_entities = resolve_entities
		self._remove_comments = remove_comments
		self._remove_pis = remove_pis
		self._strip_cdata = strip_cdata
		self._parser = None
		self.version = EXPAT_VERSION.capitalize()

	def feed(self, data):
		if self._parser is None:
			self.__initparser()
#			if data.startswith('\xFF\xFE'): do something with BOM if we really care
		self._parser.Parse(data, False)

	def close(self):
		self._parser.Parse('', True)
		return self.__closeparser()[0]  # ElementTree compatible return

	def parsefile(self, file):
		self.__initparser()
		self._parser.ParseFile(file)
		self.__xmldecl['URL'] = getattr(file, 'name', None)
		return self.__closeparser()

	def __initparser(self):
		# apparently the expat parser can't be reused so we must
		# recreate it each time we parse a file
		parser = expat.ParserCreate(self._encoding, '}')
		parser.SetParamEntityParsing(expat.XML_PARAM_ENTITY_PARSING_NEVER)
		parser.XmlDeclHandler = self.__handle_xmldecl
		parser.StartDoctypeDeclHandler = self.__handle_startdtd
		parser.ElementDeclHandler = self.__handle_elemdecl
		parser.AttlistDeclHandler = self.__handle_attldecl
		parser.EntityDeclHandler = self.__handle_entitydecl
		parser.NotationDeclHandler = self.__handle_notndecl
		parser.EndDoctypeDeclHandler = self.__handle_enddtd
		parser.StartElementHandler = self.__handle_startelem
		parser.CharacterDataHandler = self.__handle_data
		parser.EndElementHandler = self.__handle_endelem
		parser.StartCdataSectionHandler = self.__handle_startcdata
		parser.EndCdataSectionHandler = self.__handle_endcdata
		parser.CommentHandler = self.__handle_comment
		parser.ProcessingInstructionHandler = self.__handle_pi
		parser.StartNamespaceDeclHandler = self.__handle_startns
#		parser.EndNamespaceDeclHandler = self.__handle_endns
		if self._resolve_entities:
			parser.DefaultHandlerExpand = self.__handle_default
			parser.ExternalEntityRefHandler = self.__handle_extent
		else:
			parser.DefaultHandler = self.__handle_default
			# By setting neither the DefaultExpand nor ExternalEntity
			# handlers, all entity references should get passed unmodified
			# to our Default handler. However, entity references inside of
			# attribute values are still being expanded by expat.
		parser.buffer_text = True
		parser.returns_unicode = True      # we want everything in Unicode
		parser.ordered_attributes = False  # ElementTree won't preserve attribute order, unfortunately
		parser.specified_attributes = not self._attribute_defaults
		self._parser = parser
		if self._mytarget:
			# initialize internal treebuilder data structure
			self.__last = self.factory(DocInfo)
			self.__estk = [self.__last]
			self.__dstk = []
			self.__tail = False
		else:
			self.__start(DocInfo, {})
		self.__nstk = []
		self.__xmldecl = {}

	def __closeparser(self):
		self._parser = None
		self.__stop(DocInfo)
		if self._mytarget:
			doc = self.__last
			del self.__last, self.__estk, self.__dstk, self.__tail
		else:
			doc = self.__close()
		doc.attrib.update(self.__xmldecl)
		del self.__nstk, self.__xmldecl
		root, dtd = None, None
		for elem in doc:
			if elem.tag is DocTypeDecl:
				dtd = elem
# After re-reading the spec, it turns out that consolidating attribute
# definitions is optional, and since we don't do anything with them 
# anyway, let's just leave them the way that expat reports them.
#				# Because of the way that expat reports ATTLIST elements, we
#				# need to consolidate consecutive items with the same elemname.
#				last = dtd = elem
#				for elem in dtd:
#					if (last.tag is elem.tag is AttlistDecl and
#							last.get('elemname') == elem.get('elemname')):
#						last.get('attrdef').update(elem.get('attrdef'))
#						last.tail = elem.tail
#						dtd.remove(elem)
#					else:
#						last = elem
			elif isinstance(elem.tag, basestring):
				root = elem
				break
		return root, doc, dtd

	def __handle_xmldecl(self, version, encoding, standalone):
		self.__xmldecl = dict(xml_version=version.encode(),
							  encoding=encoding and encoding.encode(),
							  standalone=bool(standalone) if standalone >= 0 else None)

	def __handle_startdtd(self, rootname, systemid, publicid, internalset):
		self.__start(DocTypeDecl, dict(root_name=rootname, system_url=systemid,
									   public_id=publicid, internal_set=internalset))

	def __handle_enddtd(self):
		self.__stop(DocTypeDecl)

	def __handle_elemdecl(self, name, model):
		self.__start(ElementDecl, dict(name=name, model=model))
		self.__stop(ElementDecl)

	def __handle_attldecl(self, elemname, attrname, attrtype, default, required):
		self.__start(AttlistDecl, dict(elemname=elemname, attrname=attrname,
									   type=attrtype, default=default, required=required))
#									   attrdef={attrname:(attrtype,default,required)}))
		self.__stop(AttlistDecl)

	def __handle_entitydecl(self, name, param, value, base, systemid, publicid, notation):
		self.__start(EntityDecl, dict(name=name, value=value, param=param,
									  base=base, system_url=systemid, 
									  public_id=publicid, notation=notation))
		self.__stop(EntityDecl)

	def __handle_notndecl(self, name, base, systemid, publicid):
		self.__start(NotationDecl, dict(name=name, base=base, system_url=systemid,
										public_id=publicid))
		self.__stop(NotationDecl)

	def __handle_startns(self, prefix, uri):
		self.__nstk.append((prefix, uri))

	def __handle_startelem(self, name, attrs):
		if u'}' in name:
			name = u'{' + name
		fixattrs = {}
		for k, v in attrs.iteritems():
			fixattrs[u'{' + k if u'}' in k else k] = v
		if self.__nstk:
			for pre, uri in self.__nstk:
				fixattrs[u'xmlns:' + pre if pre else u'xmlns'] = uri or u''
			self.__nstk = []
		self.__start(name, fixattrs)

	def __handle_data(self, data):
		if not (self._remove_blank_text and data.isspace()):
			self.__data(data)

	def __handle_endelem(self, name):
		self.__stop(name)

	def __handle_endns(self, prefix):
		pass  # nothing to do here

	def __handle_startcdata(self):
		if not self._strip_cdata:
			self.__start(CDATA, {})

	def __handle_endcdata(self):
		if not self._strip_cdata:
			self.__stop(CDATA)

	def __handle_comment(self, data):
		if not self._remove_comments:
			self.__start(Comment, {})
			self.__data(data)
			self.__stop(Comment)

	def __handle_pi(self, target, data):
		if not self._remove_pis:
			self.__start(PI, {})
			self.__data(target + u' ' + data)
			self.__stop(PI)

	def __handle_default(self, data):
		if data.isspace():
			# Outside of the root element, expat reports character data
			# to the default handler instead of the normal data handler.
			# Also, expat doesn't translate line endings for the default
			# handler, so we must manually adjust in order to keep line
			# endings consistent.
			if not self._remove_blank_text:
				if u'\r\n' in data:
					data = data.replace(u'\r\n', u'\n')
				self.__data(data)
		elif data[0] == u'&' and data[-1] == u';':
			self.__start(Entity, {})
			self.__data(data)
			self.__stop(Entity)
		else:
			# Nothing else should be reported here, as we are catching
			# everything else in the other handlers. However, I have 
			# seen some spurious text reported when expat encounters
			# complex DOCTYPE declarations. Apparently the best thing to
			# do is just ignore it.
			pass

	def __handle_extent(self, context, base, systemid, publicid):
		# This should cause expat to raise an exception since we
		# don't support parsing external entity references.
		return 0

	def __start(self, tag, attrs):
		self.__flush()
		self.__last = elem = self.factory(tag, attrs)
		self.__estk[-1].append(elem)
		self.__estk.append(elem)
		self.__tail = False

	def __data(self, data):
		self.__dstk.append(data)

	def __stop(self, tag):
		self.__flush()
		self.__last = self.__estk.pop()
		self.__tail = True

	def __flush(self):
		if self.__dstk:
			text = ''.join(self.__dstk)
			if self.__tail:
				self.__last.tail = text
			else:
				self.__last.text = text
			self.__dstk = []

class ElementTree(_ElementTree):

	def __init__(self, elem=None, file=None,
				 # lxml argument
				 parser=None):
		if elem is not None and file is not None:
			raise ValueError('constructor takes either element or file, not both')
		if parser is None:
			parser = XMLParser()
		self._parser = parser
		if elem is not None:
			self._setroot(elem)
		elif file is not None:
			self.parse(file)
		else:
			self._root = self._doc = self._dtd = None

	@property
	def docinfo(self):
		return DocInfo(self) if self._root is not None else None

	def getroot(self):
		return self._root

	def _setroot(self, elem):
		if not iselement(elem):
			raise TypeError('%r object is not an Element' % type(elem).__name__)
		self._root = elem
		self._doc = None
		self._dtd = None

	def parse(self, source, parser=None):
		if hasattr(source, "read"):
			file = source
		else:
			file = open(source, "rt")
		try:
			if parser is not None:
				self._parser = parser
			else:
				parser = self._parser
			if hasattr(parser, 'parsefile'):
				self._root, self._doc, self._dtd = parser.parsefile(file)
			else:
				while True:
					data = file.read(65536)
					if not data: break
					parser.feed(data)
				self._root, self._doc, self._dtd = parser.close(), None, None
		finally:
			if source is not file:
				file.close()

	def write(self, dest, encoding=None, xml_declaration=None,
			  default_namespace=None, method=None,
			  # lxml arguments
			  pretty_print=False, with_tail=True, standalone=None):
		if self._root is None:
			raise ValueError('ElementTree not initialized')
		if method is None:
			method = 'xml'
		elif method not in ('xml', 'html', 'text'):
			raise ValueError('unknown method %r' % method)
#		if encoding is None:
#			encoding = 'US-ASCII'
#		elif encoding is not unicode:
#			# raise LookupError if encoding is unknown before we overwrite the file
#			u''.encode(encoding)
		if not hasattr(dest, 'write'):   # a filename or fildescriptor
			if encoding is None:
				encoding = 'UTF-8'
			ios = io.open(dest, 'wt', encoding=encoding, errors='xmlcharrefreplace')
		elif not isinstance(file, io.TextIOBase): # an old-style file-like object
			if encoding is None:
				encoding = 'UTF-8'
			ios = _iotextwrapper(dest, encoding=encoding, errors='xmlcharrefreplace')
		else:                               # a new-style io stream
			if encoding is None:
				encoding = dest.encoding
			elif encoding != dest.encoding:
				raise ValueError("encoding doesn't match file encoding")
			ios = dest
		write = ios.write
		try:
#			if encoding is unicode:
#				xml_declaration = False
#				write = file.write
#			else:
#				_write = file.write
#				if encoding.upper() in ('UTF-16', 'UTF-32'):
#					_encoding = encoding + '-LE'
#					_write(u'\uFEFF'.encode(_encoding))  # BOM
#				else:
#					_encoding = encoding
#				write = lambda text: _write(text.encode(_encoding))
			doc, root = self._doc, self._root
			if method == 'xml':
				_serialize_xmldecl(doc, write, pretty_print)
#				# It might make sense to use the standalone flag from the document
#				# if it's not specified here, but lxml doesn't do that so we won't.
#				#if standalone is None:
#				#	standalone = doc.standalone
#				if standalone is not None:
#					standalone = u' standalone="%s"' % (u'yes' if standalone else u'no')
#				else:
#					standalone = u''
#				if xml_declaration is None:
#					xml_declaration = standalone or encoding.upper() not in ('US-ASCII', 'UTF-8', 'UTF-16')
#				if xml_declaration:
#					write(u'<?xml version="1.0" encoding="%s"%s?>' % (encoding, standalone))
#					if doc is not None and doc.text:
#						write(doc.text)
#					elif pretty_print:
#						write(u'\n')
			if doc is not None and method != 'text':
				for elem in doc:
					if isinstance(elem.tag, basestring):
						_serialize_elem(elem, write, pretty_print, method)
					else:
						elem.tag._serialize(elem, write, pretty_print)
					if elem.tail:
						write(elem.tail)
					elif pretty_print:
						write(u'\n')
			else:
				_serialize_elem(root, write, pretty_print, method)
				if with_tail and root.tail:
					write(root.tail)
		finally:
			if ios is not dest:
				ios.close()

_default_nsmap = {u'http://www.w3.org/XML/1998/namespace': u'xml',
				  u'http://www.w3.org/2000/xmlns/': u'xmlns',
				  None: None,  # special entry for default namespace
				  int: 0}      # special entry for temporary namespace count

def _serialize_xmldecl(xmldecl, encoding, standalone, doc, write, p):
	# It might make sense to use the standalone flag from the document
	# if it's not specified here, but lxml doesn't do that so we won't.
	#if standalone is None:
	#	standalone = doc.standalone
	if standalone is not None:
		standalone = u' standalone="%s"' % (u'yes' if standalone else u'no')
	else:
		standalone = u''
	if xml_declaration is None:
		xml_declaration = standalone or encoding.upper() not in ('US-ASCII', 'UTF-8', 'UTF-16')
	if xml_declaration:
		write(u'<?xml version="1.0" encoding="%s"%s?>' % (encoding, standalone))
		if doc is not None and doc.text:
			write(doc.text)
		elif pretty_print:
			write(u'\n')

def _serialize_elem(elem, write, pretty, method, nsmap=None):

	def escape_attrval(text):
		if u'&' in text:
			text = text.replace(u'&', u'&amp;')
		if u'<' in text:
			text = text.replace(u'<', u'&lt;')
		if u'>' in text:
			text = text.replace(u'>', u'&gt;')
		if u'"' in text:
			text = text.replace(u'"', u'&quot;')
		if u'\n' in text:
			text = text.replace(u'\n', u'&#10;')
		return text

	def escape_cdata(text):
		if u'&' in text:
			text = text.replace(u'&', u'&amp;')
		if u'<' in text:
			text = text.replace(u'<', u'&lt;')
		if u'>' in text:
			text = text.replace(u'>', u'&gt;')
		return text

	def add_tempns(uri, nsmap, tempnslst):
		idx = nsmap[int]
		nsmap[int] = idx + 1
		prefix = u'_ns%d' % idx
		nsmap[uri] = prefix
		tempnslst.append((prefix, uri))
		return prefix

	tag, text = elem.tag, elem.text
	if method == 'text':
		if isinstance(tag, basestring) or tag in (CDATA, Entity):
			if text:
				write(text)
			for child in elem:
				_serialize_elem(child, write, pretty, method)
				if child.tail:
					write(child.tail)
	elif isinstance(tag, basestring):
		html = method == 'html'
		if nsmap is None:
			nsmap = _default_nsmap
		tempnslst = []
		attritems = elem.items()
		# update nsmap with any namespace declarations in this element
		for attr, val in attritems:
			if attr[:5] == u'xmlns':
				prefix = attr[6:]
				# this is somewhat obscure, in case the user re-defines a
				# ns prefix that is already defined in the document
				nsmap = dict((k, v) for k, v in nsmap.iteritems() if v != prefix)
				if prefix:
					nsmap[val] = prefix
				else:
					nsmap[None] = val
		if tag[:1] == u'{':
			uri, tag = tag[1:].rsplit(u'}', 1)
			# check if uri matches the default namespace
			if uri != nsmap[None]:
				try:
					prefix = nsmap[uri]
				except KeyError:
					prefix = add_tempns(uri, nsmap, tempnslst)
				tag = prefix + u':' + tag
		write(u'<' + tag)
		for attr, val in attritems:
			if attr[:1] == u'{':
				uri, attr = attr[1:].rsplit(u'}', 1)
				# attributes don't use the default namespace
				try:
					prefix = nsmap[uri]
				except KeyError:
					prefix = add_tempns(uri, nsmap, tempnslst)
				attr = prefix + u':' + attr
			write(u' %s="%s"' % (attr, escape_attrval(val)))
		# write out temporary ns declarations after other attributes because
		# new declarations may have been added while processing attributes
		for prefix, uri in tempnslst:
			write(u' xmlns:%s="%s"' % (prefix, escape_attrval(uri)))
		if text or len(elem) or html:
			write(u'>')
			if text:
				if html and tag.lower() in (u'script', u'style'):
					write(text)
				else:
					write(_escape_cdata(text))
			for child in elem:
				_serialize_elem(child, write, pretty, method, nsmap)
				if child.tail:
					write(_escape_cdata(child.tail))
				elif pretty:
					write(u'\n')
			if not (html and tag.lower() in HTML_EMPTY):
				write(u'</%s>' % tag)
		else:
			write(u' />')
	else:
		tag._serialize(elem, write, pretty)

def _serialize_externid(elem, write, pretty=False, repr=False):
	publicid = elem.get('public_id') or u''
	systemid = elem.get('system_url') or u''
	if pretty: pretty = len(publicid) + len(systemid) > 40
	if publicid and systemid:
		if pretty: write(u'\n        ')
		write(u' PUBLIC "%s"' % publicid)
		if pretty: write(u'\n               ')
		write(u' "%s"' % systemid)
	elif publicid:
		if pretty: write(u'\n        ')
		write(u' PUBLIC "%s"' % publicid)
	elif systemid:
		if pretty: write(u'\n        ')
		write(u' SYSTEM "%s"' % systemid)

class DocInfo(object):

	def __init__(self, tree):
		self._tag = tree._root.tag
		self._doc = tree._doc if tree._doc is not None else {}
		self._dtd = tree._dtd if tree._dtd is not None else {}

	URL = property(lambda self: self._doc.get('URL'))
	xml_version = property(lambda self: self._doc.get('xml_version') or '1.0')
	encoding = property(lambda self: self._doc.get('encoding') or 'UTF-8') # could be UTF-16, but how to know?
	standalone = property(lambda self: self._doc.get('standalone'))
	doc_elem = property(lambda self: self._doc if self._doc != {} else None)
	# lxml returns the actual root element tag regardless of what the DTD
	# states, so we will do the same
	root_name = property(lambda self: self._tag)
	public_id = DocTypeDecl.public_id
	system_url = DocTypeDecl.system_url
	internalDTD = DocTypeDecl.internalDTD
	externalDTD = DocTypeDecl.externalDTD

	@property
	def doctype(self):
		if self._dtd == {}: return None
		buf = []
		DocTypeDecl._serialize(self._dtd, buf.append, repr=True)
		return u''.join(buf)

def decltype(cls):
	def __init__(self, elem, elname=cls.__slots__):
		setattr(self, elname, elem)
	cls.__init__ = __init__
	def __repr__(self, elname=cls.__slots__):
		buf = []
		try:
			self._serialize(getattr(self, elname), buf.append, repr=True)
		except:
			return '<%s>' % type(self).__name__
		else:
			return u''.join(buf).encode(encoding='utf-8')
	cls.__repr__ = __repr__
	for attr in cls._attrs_:
		setattr(cls, attr, property(lambda self, elname=cls.__slots__, attr=attr:
										getattr(self, elname).get(attr)))
	del cls._attrs_
	return cls

@decltype
class DocTypeDecl(object):
	__slots__ = '_dtd'
	_attrs_ = ['root_name', 'system_url', 'public_id']
	internalDTD = property(lambda self: [child.tag(child) for child in self._dtd if type(child.tag) is type]
										if self._dtd.get('internal_set') else None)
	externalDTD = property(lambda self: None)
#	doctype = property(lambda self: u'<!DOCTYPE %s%s>' % (self._dtd.get('root_name'),
#						_externalid(self.public_id, self.system_url))
#						if self._dtd != {} else '')
	@staticmethod
	def _serialize(elem, write, pretty=False, repr=False):
		write(u'<!DOCTYPE %s' % elem.get('root_name'))
		_serialize_externid(elem, write, pretty, repr)
		if not repr and elem.get('internal_set'):
			write(u' [')
			if elem.text:
				write(elem.text)
			elif pretty and len(elem):
				write(u'\n')
			for child in elem:
				child.tag._serialize(child, write, pretty)
				if child.tail:
					write(child.tail)
				elif pretty:
					write(u'\n')
			write(u']>')
		else:
			write(u'>')

@decltype
class ElementDecl(object):
	__slots__ = '_elem'
	_attrs_ = ['name', 'model']
	@staticmethod
	def _serialize(elem, write, pretty=False, repr=False):
		qtext = {expat.model.XML_CQUANT_OPT: u'?',
				 expat.model.XML_CQUANT_PLUS: u'+',
				 expat.model.XML_CQUANT_REP: u'*',
				 expat.model.XML_CQUANT_NONE: u''}
		def dumpmodel(model):
			type, quant, name, children = model
			if type == expat.model.XML_CTYPE_ANY:
				return u'ANY'
			elif type == expat.model.XML_CTYPE_EMPTY:
				return u'EMPTY'
			elif type == expat.model.XML_CTYPE_NAME:
				return name + qtext[quant]
			elif type == expat.model.XML_CTYPE_CHOICE:
				return u'(%s)%s' % (u' | '.join(dumpmodel(child)
							for child in children), qtext[quant])
			elif type == expat.model.XML_CTYPE_SEQ:
				return u'(%s)%s' % (u', '.join(dumpmodel(child)
							for child in children), qtext[quant])
			elif type == expat.model.XML_CTYPE_MIXED:
				return u'(#PCDATA%s)%s' % (u' | '.join([u''] + 
							[dumpmodel(child) for child in children]),
							qtext[quant])
			else:
				return u'UNKNOWN'
		write(u'<!ELEMENT %s %s>' % (elem.get('name'), dumpmodel(elem.get('model'))))

@decltype
class AttlistDecl(object):
	__slots__ = '_elem'
	_attrs_ = ['elemname', 'attrname', 'type', 'default', 'required']
	@staticmethod
	def _serialize(elem, write, pretty=False, repr=False):
		required = elem.get('required')
		default = elem.get('default')
		if default:
			default = u'%s"%s"' % (u'#FIXED ' if required else u'', default)
		else:
			default = u'#REQUIRED' if required else u'#IMPLIED'
		write(u'<!ATTLIST %s %s %s %s>' % (elem.get('elemname'), 
										   elem.get('attrname'),
										   elem.get('type'),
										   default))
#		if not repr:
#			for name, (type, default, required) in elem.get('attrdef').iteritems():
#				if pretty: 
#					write(u'\n        ')
#				if not default:
#					write(u' %s %s %s' % (name, type, u'#REQUIRED' if required else u'#IMPLIED'))
#				else:
#					write(u' %s %s %s"%s"' % (name, type, u'#FIXED ' if required else u'', default))
#			write(u'>')
#		else:
#			write(u' ... >')

@decltype
class EntityDecl(object):
	__slots__ = '_elem'
	_attrs_ = ['name', 'value', 'param', 'base', 'system_url', 'public_id', 'notation']
	@staticmethod
	def _serialize(elem, write, pretty=False, repr=False):
		name = elem.get('name')
		if elem.get('param'):
			name = u'% ' + name
		write(u'<!ENTITY %s' % name)
		value = elem.get('value')
		if value:
			write(u' "%s"' % value)
		elif not repr:
			_serialize_externid(elem, write, pretty, repr)
		else:
			write(u' ...')
		notation = elem.get('notation')
		if notation:
			if pretty: write(u'\n        ')
			write(u' NDATA %s' % notation)
		write(u'>')

@decltype
class NotationDecl(object):
	__slots__ = '_elem'
	_attrs_ = ['name', 'base', 'system_url', 'public_id']
	@staticmethod
	def _serialize(elem, write, pretty=False, repr=False):
		write(u'<!NOTATION %s' % elem.get('name'))
		if not repr:
			_serialize_externid(elem, write, pretty, repr)
		else:
			write(u'...')
		write(u'>')

del decltype

def CDATA(text=None):
	elem = Element(CDATA)
	elem.text = text
	return elem

def Entity(text=None):
	elem = Element(Entity)
	elem.text = text
	return elem

CDATA._serialize = (lambda elem, write, pretty=False, repr=False:
						write(u'<![CDATA[%s]]>' % (elem.text or '')))
Entity._serialize = (lambda elem, write, pretty=False, repr=False:
						write(u'%s' % (elem.text or '')))
Comment._serialize = (lambda elem, write, pretty=False, repr=False:
						write(u'<!--%s-->' % (elem.text or '')))
PI._serialize = (lambda elem, write, pretty=False, repr=False:
						write(u'<?%s?>' % (elem.text or '')))

def _iotextwrapper(file, encoding, errors):
	class IOWrapper(object):
		def __init__(self, file):
			self.file = file
		def readable(self):
			return 'r' in self.file.mode
		def writable(self):
			return 'w' in self.file.mode
		def seekable(self):
			return False
		def read(self, n=-1):
			return self.file.read(n)
		def write(self, b):
			return self.file.write(b)
		def flush(self):
			pass
		def close(self):
			return self.file.close()
		closed = property(lambda self: self.file.closed)
	if not isinstance(file, io.IOBase):
		file = IOWrapper(file)
	newlines = None if 'b' in file.mode else ''
	return io.TextIOWrapper(file, encoding, errors, nelines, False)
